//
// Created by hujin on 2020/10/24.
//

#include "ast_statements.h"
#include "ast_function.h"
#include "../logger.h"

bool FuncCallNode::checkError(SymbolTable *table) {
    auto disc = table->getDiscriptions(name);
    bool err = false;
    if (disc.type != ID_Type::ID_FUNC_C && disc.type != ID_Type::ID_FUNC_I)
        err = true, logError("Function call: not function name.");
    else {
        const std::vector<std::pair<bool, std::string>> &params1 = disc.paramsNode->getParams();
        const std::vector<ExprNode *> &params2 = params->getParams();
        if (params1.size() != params2.size()) {
            err = true; logError("Function call: params count mismatch.");
            log_error2('d', getLine());//函数调用时实参个数大于或小于形参个数
        }
        int i = 0;
        while(i < params1.size() && i < params2.size()){
            if(params1[i].first != params2[i]->isReturnInt()){
                err = true; logError("Function call: param type mismatch.");
                log_error2('e', getLine());//函数参数类型不匹配
                break;
            }
            i++;
        }
    }
    return !err;
}

void FuncCallNode::genCode(Synthesizer *out, MemoryAllocator *mem) {


    auto label = getLabelName(name);
    mem->saveRegs({/*TODO */});
    params->genCode(out, mem);
    mem->push_stack();
    out->text<<"jal "<<label<<std::endl;
    mem->pop_stack();
}

bool VoidCallNode::checkError(SymbolTable *table) {
    auto disc = table->getDiscriptions(name);
    bool err = false;
    if (disc.type != ID_Type::ID_VOID)
        err = true, logError("Void function call: not void function name.");
    else {
        const std::vector<std::pair<bool, std::string>> &params1 = disc.paramsNode->getParams();
        const std::vector<ExprNode *> &params2 = params->getParams();
        if (params1.size() != params2.size()) {
            err = true; logError("Function call: params count mismatch.");
            log_error2('d', getLine());//函数调用时实参个数大于或小于形参个数
        }
        int i = 0;
        while(i < params1.size() && i < params2.size()){
            if(params1[i].first != params2[i]->isReturnInt()){
                err = true; logError("Function call: param type mismatch.");
                log_error2('e', getLine());//函数参数类型不匹配
                break;
            }
            i++;
        }
    }
    return !err;
}

bool ConditionNode::checkError(SymbolTable *table) {
    if(!left->isReturnInt() || !right->isReturnInt()){
        logError("condition expression returning char.");
        log_error2('f', getLine()); //条件判断的左右表达式只能为整型，其中任一表达式为字符型即报错，例如’a’==1
        return false;
    }
    return true;
}

std::string getRevCode(Symbol op){
    switch (op) {
        case EQL:return "bne";
        case NEQ:return "beq";
        case GEQ:return "blt";
        case LEQ:return "bgt";
        case GRE:return "ble";
        case LSS:return "bge";
        default:return "beq";
    }
}

std::string getLabel(int labelId){
    std::string ret = "label_";
    ret.append(std::to_string(labelId));
    return ret;
}
MemoryAllocator *ConditionNode::genCode(Synthesizer *out, MemoryAllocator *allocator, int labelId) {
    left->genCode(out, allocator, tempIDLeft);
    right->genCode(out, allocator, temppIdRight);
    int regL = allocator->loadToReg(tempIDLeft, false);
    int regR = allocator->loadToReg(temppIdRight, false);
    out->text<<getRevCode(op)<<" $"<<regL<<" $"<<regR<<" "<<getLabel(labelId)<<std::endl;
    auto *subMemAlloc = allocator->split();
    return subMemAlloc;
}

void IfNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    if(ifStatement == nullptr && elseStatement == nullptr) return ;
    MemoryAllocator *alloc2 = condition->genCode(out, allocator, labelId); //branch else
    if(ifStatement != nullptr) ifStatement->genCode(out, alloc2);
    auto *codeElse = new Synthesizer();
    if(elseStatement != nullptr) {
        allocator->setSynthesizer(codeElse);
        elseStatement->genCode(codeElse, allocator);
        allocator->setSynthesizer(out);
    }
    alloc2->mergeTo(allocator, {/*TODO*/});

    if(elseStatement != nullptr) out->text<<"j "<<getLabel(labelIdEnd)<<std::endl; //j end
    out->text<<getLabel(labelId)<<": "<<std::endl; //else:
    if(elseStatement != nullptr) {
        out->concat(codeElse);
    }

    out->text << getLabel(labelIdEnd) << ": " << std::endl; //end:
    delete alloc2;
    delete codeElse;
}

void WhileNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    if(statement == nullptr)return;
    out->text<<getLabel(labelR)<<": "<<std::endl; //loop:
    MemoryAllocator *alloc = allocator->split();
    MemoryAllocator *alloc2 = condition->genCode(out, alloc, labelEnd); //branch loop_end
    statement->genCode(out, alloc2);
    alloc2->mergeTo(allocator, {/*TODO*/}); // cast reg phrase to original image
    out->text<<"j "<<getLabel(labelR)<<std::endl; //j loop
    out->text<<getLabel(labelEnd)<<": "<<std::endl; //loop_end
    alloc->mergeTo(allocator,  {});
    delete alloc2;
    delete alloc;
}

void ForNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    int index = initVarid.uid;
    initialVal->genCode(out, allocator, index);
    out->text<<getLabel(labelR)<<": "<<std::endl; //loop:
    MemoryAllocator *alloc = allocator->split();
    MemoryAllocator *alloc2 = condition->genCode(out, alloc, labelEnd); //branch loop_end
    if(statements != nullptr)statements->genCode(out, alloc2);
    int regV1 = alloc2->loadToReg(alterVar1id.uid, true);
    int regV2 = alloc2->loadToReg(alterVar2id.uid, false);
    out->text<<"addi $"<<regV1<<" $"<<regV2<<", "<<step<<std::endl; // v1 = v2 + step
    alloc2->mergeTo(allocator, {/*TODO*/});
    out->text<<"j "<<getLabel(labelR)<<std::endl; //j loop
    out->text<<getLabel(labelEnd)<<": "<<std::endl; //loop_end
    alloc->mergeTo(allocator,  {});
    delete alloc2;
    delete alloc;
}


//value
void SwitchNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    val->genCode(out, allocator, tempIndex);
    switchTableNode->genCode(out, allocator, tempIndex);
}

void SwitchTableNode::genCode(Synthesizer *out, MemoryAllocator *allocator, int tempIndex){
    int pos = 0;
    int reg = allocator->loadToReg(tempIndex, false);
    for(auto x: cases){
        out->text<<"beq $"<<reg<<", "<<x.first<<", "<<getLabel(labels[pos++])<<std::endl; //branch label_x
    }
    pos = 0;
    auto *alloc = allocator->split();
    if(defaultStat != nullptr) defaultStat->genCode(out, allocator);
    out->text<<"j "<<getLabel(labelRet)<<std::endl;//j label_ret

    for(auto x: cases){
        out->text<<getLabel(labels[pos++])<<":"<<std::endl;//label_x:
        auto *alloc2 = alloc->split();
        if(x.second != nullptr)x.second->genCode(out, alloc2);
        alloc2->mergeTo(allocator, {/*TODO*/});
        out->text<<"j "<<getLabel(labelRet)<<std::endl;//j label_ret
        delete alloc2;
    }
    delete alloc;
    out->text<<getLabel(labelRet)<<":"<<std::endl; //label_ret:
}

bool SwitchTableNode::containsReturn(bool noVal,bool  retInt) {
    for(auto x : cases){
        if(x.second != nullptr && x.second->containsReturn(noVal, retInt))return true;
    }
    return defaultStat != nullptr && defaultStat->containsReturn(noVal, retInt);
}

bool StatListNode::containsReturn(bool noVal, bool retInt) {
    for(auto x : statements){
        if(x != nullptr && x->containsReturn(noVal, retInt))return true;
    }
    return false;
}

void StatListNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    for(auto *x : statements) if(x != nullptr) x->genCode(out,allocator);
}

bool ReturnNode::containsReturn(bool noVal, bool retInt) {
    if(noVal)return expr == nullptr;
    return expr != nullptr && retInt == expr->isReturnInt();
}

void ReturnNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    if(insideMain){
        out->text<<"li $v0, 10"<<std::endl;
        out->text<<"syscall"<<std::endl;
        return;
    }
    if(expr != nullptr ){
        //allocator->setToSpecialReg(tempId, getRegvx(0));
        expr->genCode(out, allocator, tempId);
        allocator->moveToSpecialReg(tempId, getRegvx(0));
    }
    allocator->save_global_changes();
    out->text<<"jr $31"<<std::endl;
}


void AssignNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    if(target->isArr()){
        value->genCode(out, allocator, indexExpTemp);
        target->codeSaveArr(out, allocator, indexExpTemp);
    } else{
        int index = target->getVarIndex();
        int indexTemp = value->getUseVarIndex().count(index) ? indexExpTemp : index ;
        value->genCode(out, allocator, indexTemp);
        if(indexTemp == indexExpTemp){
            allocator->move(indexTemp, index);
        }
    }
}

void PrintfNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    if(str.empty()){ // printf(a)
        int regA0 = getRegax(0);
        allocator->setToSpecialReg(var_tmp, regA0);
        expr->genCode(out, allocator, var_tmp);
        allocator->moveToSpecialReg(var_tmp, regA0);
        allocator->useReg(getRegvx(0));
        out->text<<"li $v0, "<<(expr->isReturnInt()? 1:11)<<std::endl;
        out->text<<"syscall"<<std::endl;
        allocator->useReg(getRegax(0));
        out->text<<"li $a0, "<<((int)'\n')<<std::endl;
        out->text<<"li $v0, "<<11<<std::endl;
        out->text<<"syscall"<<std::endl;
        allocator->releaseReg(var_tmp);
    }else{ // printf("x"
        if(expr == nullptr) {
            out->data << "str" << -var_tmp << ":    .asciiz \"" << str << ((expr == nullptr) ? "\\n\"" : "\"")
                      << std::endl;
            allocator->useReg(getRegax(0));
            out->text << "la $a0, " << "str" << -var_tmp << std::endl;
            allocator->useReg(getRegvx(0));
            out->text << "li $v0, " << 4 << std::endl;
            out->text << "syscall" << std::endl;
        }
        else { //printf("x", x)
            int regA0 = getRegax(0);
            //allocator->setToSpecialReg(var_tmp, regA0);
            expr->genCode(out, allocator, var_tmp);
            out->data << "str" << -var_tmp << ":    .asciiz \"" << str << ((expr == nullptr) ? "\\n\"" : "\"")
                      << std::endl;
            allocator->useReg(getRegax(0));
            out->text << "la $a0, " << "str" << -var_tmp << std::endl;
            allocator->useReg(getRegvx(0));
            out->text << "li $v0, " << 4 << std::endl;
            out->text << "syscall" << std::endl;
            allocator->moveToSpecialReg(var_tmp, regA0);
            out->text<<"li $v0, "<<(expr->isReturnInt()? 1:11)<<std::endl;
            out->text<<"syscall"<<std::endl;
            out->text<<"li $a0, "<<((int)'\n')<<std::endl;
            out->text<<"li $v0, "<<11<<std::endl;
            out->text<<"syscall"<<std::endl;
        }
        allocator->releaseReg(var_tmp);
    }
}

void ScanfNode::genCode(Synthesizer *out, MemoryAllocator *allocator) {
    if(data.type == ID_VAR_C){
        allocator->useReg(getRegvx(0));
        out->text<<"li $v0, "<<12<<std::endl;
        out->text<<"syscall"<<std::endl;
        int reg = allocator->loadToReg_writeOnly(data.uid);
        out->text<<"move $"<<reg<<", $v0" <<std::endl;
    }
    else {
        allocator->useReg(getRegvx(0));
        out->text<<"li $v0, "<<5<<std::endl;
        out->text<<"syscall"<<std::endl;
        int reg = allocator->loadToReg_writeOnly(data.uid);
        out->text<<"move $"<<reg<<", $v0" <<std::endl;
    }
}

void FuncCallParamsNode::genCode(Synthesizer *out, MemoryAllocator *mem) {
    int pos = 0;
    for(auto *x :params){
        x->genCode(out, mem, tempIds[pos++]);
    }
    pos = 0;
    for(auto *x :params){
        int id = tempIds[pos];
        mem->loadToParam(id, params.size());
        pos++;
    }
}
